package com.everydayflash.jiglib.pong {
	import jiglib.math.JNumber3D;
	import jiglib.physics.PhysicsSystem;
	import jiglib.physics.RigidBody;
	import jiglib.physics.constraint.JConstraintWorldPoint;
	
	import org.papervision3d.core.geom.renderables.Vertex3D;
	import org.papervision3d.core.math.Number3D;
	import org.papervision3d.core.math.Plane3D;
	import org.papervision3d.core.proto.CameraObject3D;
	import org.papervision3d.core.utils.Mouse3D;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.view.Viewport3D;
	
	import flash.events.Event;
	import flash.events.MouseEvent;	

	/**
	 * Facilitates dragging physics enabled objects in 3D space
	 * @author Reynaldo Columna aka reyco1
	 * 
	 */	
	public class StiffMouseConstraint {

		private var body:RigidBody;
		private var camera:CameraObject3D;
		private var viewport:Viewport3D;		
		private var mouse3D:Mouse3D;
		private var physics:PhysicsSystem;

		private var dragNormal:Number3D;
		private var dragPlane:Plane3D;

		
		private var leftC:JConstraintWorldPoint;
		private var rightC:JConstraintWorldPoint;
		private var bleftC:JConstraintWorldPoint;		private var brightC:JConstraintWorldPoint;

		private var yoffset:Number = 0;		private var xoffset:Number = 80;
		private var zoffset:Number = 80;
		/**
		 * Adapted from MouseConstraint by reyco
		 * 
		 */		
		public function StiffMouseConstraint(mesh:DisplayObject3D, dragNormal:Number3D, camera:CameraObject3D, viewPort:Viewport3D) {
			physics = PhysicsSystem.getInstance();

			this.body = physics.bodys[getBodyBasedOnSkin(mesh)];
			this.dragNormal = dragNormal;
			this.camera = camera;
			this.viewport = viewPort;

			Mouse3D.enabled = true;
			mouse3D = viewport.interactiveSceneManager.mouse3D;
			initialize();
		}

		private function initialize():void {
			dragPlane = new Plane3D(dragNormal, new Number3D(0, 0, 0));

			leftC = createConstraint(-xoffset, 0, 0);
			physics.addConstraint(leftC);

			rightC = createConstraint(xoffset, 0, 0);
			physics.addConstraint(rightC);
			
			bleftC = createConstraint(0, -zoffset, 0);
			physics.addConstraint(bleftC);
			
			brightC = createConstraint(0, zoffset, 0);
			physics.addConstraint(brightC);
			
			viewport.containerSprite.stage.addEventListener(Event.ENTER_FRAME, followMouse);
		}

		private function createConstraint(xo:Number, yo:Number, zo:Number):JConstraintWorldPoint {
			var pos:JNumber3D = body.currentState.position.clone();
			var offpos:JNumber3D = new JNumber3D(pos.x + xo, pos.y + yo, pos.z + zo);
			var mousep:JNumber3D = new JNumber3D(mouse3D.x - xo, mouse3D.y + yo, mouse3D.z + zo);
			var bodyPoint:JNumber3D = JNumber3D.sub(mousep, offpos);
			return new JConstraintWorldPoint(body, bodyPoint, mousep);
		}

		private function followMouse(e:Event):void {
			var cameraVertex3D:Vertex3D = new Vertex3D(camera.x, camera.y, camera.z);
			
			setWP(cameraVertex3D, -xoffset, yoffset, leftC);			setWP(cameraVertex3D, xoffset, -yoffset, rightC);
			setWP(cameraVertex3D, 0, 0, bleftC);
			setWP(cameraVertex3D, 0, 0, brightC);
		}
		
		private function setWP(cv:Vertex3D, xo:Number, yo:Number, c:JConstraintWorldPoint):void {
			var ray:Number3D = camera.unproject(viewport.containerSprite.mouseX + xo, viewport.containerSprite.mouseY + yo);
			ray = Number3D.add(ray, new Number3D(camera.x, camera.y, camera.z));			
			var rayv:Vertex3D = new Vertex3D(ray.x, ray.y, ray.z);
			var intersect:Vertex3D = dragPlane.getIntersectionLine(cv, rayv);			
			c.worldPosition = new JNumber3D(intersect.x, intersect.y, intersect.z);
		}

		private function getBodyBasedOnSkin(skin:DisplayObject3D):int {
			for (var i:String in physics.bodys) {
				if (skin == physics.bodys[i].skin.mesh) {
					return int(i);
				}
			}
			return -1;
		}

		/**
		 * Destryss the mouse constraint 
		 * 
		 */		
		public function destroy():void {
			viewport.containerSprite.stage.removeEventListener(MouseEvent.MOUSE_MOVE, followMouse);
			PhysicsSystem.getInstance().removeConstraint(leftC);			PhysicsSystem.getInstance().removeConstraint(rightC);			PhysicsSystem.getInstance().removeConstraint(bleftC);			PhysicsSystem.getInstance().removeConstraint(brightC);
			
			body.setActive();
			
			body = null;
			camera = null;
			viewport = null;		
			mouse3D = null;
			dragNormal = null;
			dragPlane = null;
			leftC = null;
		}
		public function get bend():Number {
			return yoffset;		}
		public function set bend(bend:Number):void {
			yoffset = bend;		}
	}
}